import * as path from "node:path";
import type * as _ts from "typescript";

function createParsedCommandLineByJson(
  ts: typeof _ts,
  host: _ts.LanguageServiceHost,
  rootDir: string,
  json: _ts.CompilerOptions,
  configFileName: string,
) {
  const extendedPaths = new Set<string>();
  const proxyHost = {
    ...host,
    readFile(fileName: string) {
      if (!fileName.endsWith("/package.json")) {
        extendedPaths.add(fileName);
      }
      return host.readFile(fileName);
    },
    readDirectory() {
      return [];
    },
  } as unknown as _ts.ParseConfigHost;

  // We need to require typescript/lib/typescript here because the function we need is in separated file

  const tsd: typeof _ts = require("typescript");

  const parsed = tsd.parseJsonConfigFileContent(
    json,
    proxyHost,
    rootDir,
    {},
    configFileName,
  );
  const resolver = new CompilerOptionsResolver(host.fileExists);
  for (const extendPath of [...extendedPaths].reverse()) {
    try {
      const configFile = tsd.readJsonConfigFile(extendPath, host.readFile);
      const obj = tsd.convertToObject(configFile, []);
      const rawOptions = obj?.vueCompilerOptions ?? {};
      resolver.addConfig(rawOptions, path.posix.dirname(configFile.fileName));
    } catch {
      // ignore error
    }
  }
  // ensure the rootDir is added to the config roots
  resolver.addConfig({}, rootDir);
  return {
    ...parsed,
    vueOptions: resolver.build(),
  };
}

class CompilerOptionsResolver {
  private readonly configs: _ts.CompilerOptions[] = [];

  constructor(private readonly fileExists: (path: string) => boolean) {}

  addConfig(options: _ts.CompilerOptions, rootDir: string) {
    this.configs.push({
      ...options,
      rootDir,
    });
  }

  build() {
    return this.configs.reduce(
      (prev, cur) => ({
        ...prev,
        ...cur,
      }),
      {} as _ts.CompilerOptions,
    );
  }
}
